package net.siufung.web.aop.aspect;

import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.MD5;
import com.google.common.base.Strings;
import com.google.common.util.concurrent.RateLimiter;
import lombok.AllArgsConstructor;
import net.siufung.core.exception.BizServiceException;
import net.siufung.security.api.AuthUtil;
import net.siufung.web.aop.annotation.AutoLimiter;
import net.siufung.web.aop.service.IAutoLimiterService;
import net.siufung.web.utils.WebUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;

import java.lang.reflect.Method;
import java.util.Locale;
import java.util.Objects;

/**
 * @author 陈建峰
 * @date 2022/10/19 3:12 下午
 * @since V1.0.0
 */
@Aspect
@AllArgsConstructor
public class AutoLimiterAspect {

    private final IAutoLimiterService autoLimiterService;

    @Pointcut("@annotation(net.siufung.web.aop.annotation.AutoPermit)")
    public void autoPermitPointCut() {}

    @Around("autoPermitPointCut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        Object proceed = proceedingJoinPoint.proceed();
        MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
        Method method = signature.getMethod();
        AutoLimiter autoLimiter = method.getAnnotation(AutoLimiter.class);
        if(Objects.nonNull(autoLimiter)){
            String autoLimiterKey = getAutoLimiterKey(autoLimiter, method);
            RateLimiter rateLimiter = autoLimiterService.getCache(autoLimiterKey);
            if(Objects.isNull(rateLimiter)){
                rateLimiter = RateLimiter.create(autoLimiter.perSecond());
                autoLimiterService.putCache(autoLimiterKey, rateLimiter,
                        autoLimiter.timeout(), autoLimiter.timeOutUnit());
            } else {
                if(rateLimiter.tryAcquire(autoLimiter.timeout(), autoLimiter.timeOutUnit())){
                    throw new BizServiceException("访问人数太多,请稍后再试试");
                };
            }
        }

        return proceed;
    }

    private String getAutoLimiterKey(AutoLimiter autoLimiter, Method method){
        StringBuilder autoLimiterKeyBuilder = new StringBuilder(autoLimiter.key());
        switch (autoLimiter.autoLimiterType()) {
            case IP:
                String ip = WebUtil.getIp();
                if(Strings.isNullOrEmpty(ip)){
                    throw new BizServiceException("ip获取失败，不支持IP限流类型！");
                }
                autoLimiterKeyBuilder.append(autoLimiter.autoLimiterType().name().toLowerCase(Locale.ROOT))
                        .append(MD5.create().digestHex16(ip));
                break;
            case USER_ID:
                String userUniqueKey = AuthUtil.getUserId();
                if(Strings.isNullOrEmpty(userUniqueKey)){
                    throw new BizServiceException("用户不存在，不支持用户限流类型！");
                }
                autoLimiterKeyBuilder.append(autoLimiter.autoLimiterType().name().toLowerCase(Locale.ROOT))
                        .append(MD5.create().digestHex16(userUniqueKey));
                break;
            default:
                break;
        }
        autoLimiterKeyBuilder.append(MD5.create().digestHex16(
                method.getDeclaringClass().getName() + StrUtil.DOT + method.getName()));
        return autoLimiterKeyBuilder.toString();
    }

}
